﻿using System;
using System.Linq;
using System.Data.Entity.Database;
using System.Transactions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NpCrm.DataAccess.Repositories;

namespace NpCrm.Domain.Tests
{
    [TestClass]
    public class ContactRepositoryTests
    {
        private TransactionScope _scope;

        [TestInitialize]
        public void TestSetup()
        {
            DbDatabase.SetInitializer(new DropCreateDatabaseIfModelChanges<NpCrm.DataAccess.Core.NpCrmDataContext>());
            _scope = new TransactionScope(TransactionScopeOption.RequiresNew,
                                          new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted });
        }

        [TestCleanup]
        public void TestTeardown()
        {
            if (_scope != null)
                //_scope.Complete();
                _scope.Dispose();
        }

        public static Contact AddFakeContact()
        {
            using (var repo = new ContactRepository())
            {
                var fakeContact = GetMockContact();
                repo.Add(fakeContact);
                repo.Commit();
                Assert.IsTrue(fakeContact.ContactId > 0);
                return fakeContact;
            }
        }

        [TestMethod]
        public void CanAdd()
        {
            AddFakeContact();
        }

        [TestMethod]
        public void CanGetById()
        {
            var fakeContact = AddFakeContact();
            //separate unit of work
            using(var contactRepo = new ContactRepository())
            {
                var persistedContact = contactRepo.GetById(fakeContact.ContactId);
                Assert.IsNotNull(persistedContact);
                Assert.AreNotSame(fakeContact, persistedContact);
                CompareContacts(fakeContact, persistedContact);
            }
        }

        [TestMethod]
        public void CanUpdate()
        {
            //first, store a fake contact
            var fakeContact = AddFakeContact();
            //then get it and update it
            Contact updateContact;
            using (var contactRepo = new ContactRepository())
            {
                updateContact = contactRepo.GetById(fakeContact.ContactId);
                updateContact.DaytimePhone = "9999999999";
                updateContact.FirstName = "Vern";
                updateContact.LastName = "Gus";
                updateContact.UpdateDate = DateTime.Now;
                updateContact.UpdateUserId = "MisterMan";
                contactRepo.Commit();
            }
            //verify update
            using (var contactRepo = new ContactRepository())
            {
                var updatedContact = contactRepo.GetById(updateContact.ContactId);
                Assert.IsNotNull(updatedContact);
                Assert.AreNotSame(updateContact, updatedContact);
                CompareContacts(updateContact, updatedContact);
            }
        }

        [TestMethod]
        public void CanDelete()
        {
            var fakeContact = AddFakeContact();
            using (var contactRepo = new ContactRepository())
            {
                var contactToDelete = contactRepo.GetById(fakeContact.ContactId);
                contactRepo.Delete(contactToDelete);
                contactRepo.Commit();
            }
            using (var contactRepo = new ContactRepository())
            {
                var shouldBeNull = contactRepo.GetById(fakeContact.ContactId);
                Assert.IsNull(shouldBeNull);
            }
        }


        [TestMethod]
        public void CanGetByLastName()
        {
            var fakeContact = AddFakeContact();
            using (var contactRepo = new ContactRepository())
            {
                var persistedContact = contactRepo.GetByName(fakeContact.LastName).FirstOrDefault();
                Assert.IsNotNull(persistedContact);
                Assert.AreNotSame(fakeContact, persistedContact);
                CompareContacts(fakeContact, persistedContact);
            }
        }

        [TestMethod]
        public void CanGetByLastAndFirstName()
        {
            var fakeContact = AddFakeContact();
            using (var contactRepo = new ContactRepository())
            {
                var persistedContact =
                    contactRepo.GetByName(fakeContact.LastName, fakeContact.FirstName).FirstOrDefault();
                Assert.IsNotNull(persistedContact);
                Assert.AreNotSame(fakeContact, persistedContact);
                CompareContacts(fakeContact, persistedContact);
            }
        }

        [TestMethod]
        public void CanGetByTaxId()
        {
            var fakeContact = AddFakeContact();
            using (var contactRepo = new ContactRepository())
            {
                var persistedContact =
                    contactRepo.GetByTaxId(fakeContact.TaxIdNumber).FirstOrDefault();
                Assert.IsNotNull(persistedContact);
                Assert.AreNotSame(fakeContact, persistedContact);
                CompareContacts(fakeContact, persistedContact);
            }
        }

        [TestMethod]
        public void CanGetByHomePhone()
        {
            CanGetByPhone("HomePhone");
        }

        [TestMethod]
        public void CanGetByDaytimePhone()
        {
            CanGetByPhone("DaytimePhone");
        }

        [TestMethod]
        public void CanGetByMobilePhone()
        {
            CanGetByPhone("MobilePhone");
        }

        [TestMethod]
        public void CanGetByPrimaryEmail()
        {
            CanGetByEmail("PrimaryEmail");
        }

        [TestMethod]
        public void CanGetBySecondaryEmail()
        {
            CanGetByEmail("SecondaryEmail");
        }

        [TestMethod]
        public void CanAssociateAddress()
        {
            var fakeContact = AddFakeContact();
            var fakeAddress = AddressRepositoryTests.AddFakeAddress();
            using(var contactRepo = new ContactRepository())
            {
                var persistedContact = contactRepo.GetById(fakeContact.ContactId);
                Assert.IsNotNull(persistedContact);
                persistedContact.Addresses.Add(fakeAddress);
                contactRepo.Commit();
            }
            using (var contactRepo = new ContactRepository())
            {
                var updatedContact = contactRepo.GetById(fakeContact.ContactId);
                Assert.IsNotNull(updatedContact);
                Assert.IsNotNull(updatedContact.Addresses);
                Assert.IsTrue(updatedContact.Addresses.Count > 0);
                AddressRepositoryTests.CompareAddresses(fakeAddress, updatedContact.Addresses.FirstOrDefault());
            }
        }

        [TestMethod]
        public void CanAssociateDonation()
        {
            var fakeDonation = DonationRepositoryTests.AddFakeDonation();
            using (var contactRepo = new ContactRepository())
            {
                var updatedContact = contactRepo.GetById(fakeDonation.Contact.ContactId);
                Assert.IsNotNull(updatedContact);
                Assert.IsTrue(updatedContact.Donations.Count > 0);
                DonationRepositoryTests.CompareDonations(fakeDonation, updatedContact.Donations.FirstOrDefault());
            }
        }

        private void CanGetByPhone(string phonePropertyName)
        {
            var fakeContact = AddFakeContact();
            using (var contactRepo = new ContactRepository())
            {
                var phoneToSearchOn =
                    fakeContact.GetType().GetProperty(phonePropertyName,
                                                      typeof (string)).GetValue(fakeContact, null) as string;
                Assert.IsNotNull(phoneToSearchOn);
                var persistedContact = contactRepo.GetByPhone(phoneToSearchOn).FirstOrDefault();
                Assert.IsNotNull(persistedContact);
                Assert.AreNotSame(fakeContact, persistedContact);
                CompareContacts(fakeContact, persistedContact);
            }
        }

        private void CanGetByEmail(string emailPropertyName)
        {
            var fakeContact = AddFakeContact();
            using (var contactRepo = new ContactRepository())
            {
                var emailToSearchOn =
                    fakeContact.GetType().GetProperty(emailPropertyName,
                                                      typeof (string)).GetValue(fakeContact, null) as string;
                var persistedContact = contactRepo.GetByEmail(emailToSearchOn).FirstOrDefault();
                Assert.IsNotNull(persistedContact);
                Assert.AreNotSame(fakeContact, persistedContact);
                CompareContacts(fakeContact, persistedContact);
            }
        }

        private static void CompareContacts(Contact expected, Contact actual)
        {
            Assert.AreEqual(expected.CreateUserId, actual.CreateUserId);
            Assert.AreEqual(expected.DaytimePhone, actual.DaytimePhone);
            Assert.AreEqual(expected.HomePhone, actual.HomePhone);
            Assert.AreEqual(expected.MobilePhone, actual.MobilePhone);                
            Assert.AreEqual(expected.FirstName, actual.FirstName);
            Assert.AreEqual(expected.IsFemale, actual.IsFemale);
            Assert.AreEqual(expected.LastName, actual.LastName);
            Assert.AreEqual(expected.MiddleName, actual.MiddleName);
            Assert.AreEqual(expected.NickName, actual.NickName);
            Assert.AreEqual(expected.PrimaryEmail, actual.PrimaryEmail);
            Assert.AreEqual(expected.SecondaryEmail, actual.SecondaryEmail);
            Assert.AreEqual(expected.TaxIdNumber, actual.TaxIdNumber);
        }

        private static Contact GetMockContact()
        {
            return new Contact
                       {
                           CreateDate = DateTime.Now,
                           CreateUserId = "artjohns",
                           DaytimePhone = "123-456-7890",
                           FirstName = "Arthur",
                           MiddleName = "Andrew",
                           LastName = "Johnson",
                           HomePhone = "(987)654-3210",
                           IsFemale = false,
                           MobilePhone = "5555555555",
                           PrimaryEmail = "art@johnson.com",
                           SecondaryEmail = "johnson@art.com",
                           TaxIdNumber = "748-392-0192",
                           NickName = "AJ"
                       };
        }
    }
}
